﻿@* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True *@
@using System
@using System.Collections.Generic
@using System.Linq
@using Hangfire
@using Hangfire.Common
@using Hangfire.Dashboard
@using Hangfire.Dashboard.Pages
@using Hangfire.Dashboard.Resources
@using Hangfire.States
@using Hangfire.Storage
@using Hangfire.Storage.Monitoring
@inherits RazorPage
@{
    var monitor = Storage.GetMonitoringApi();
    var job = monitor.JobDetails(JobId);
    var dto = job != null ? new JobDetailsRendererDto(this, JobId, job) : null;
    
    string title = null;

    if (job != null)
    {
        title = job.Job != null ? Html.JobName(job.Job) : null;

        var historyList = new List<StateHistoryDto>(job.History);
        historyList.Add(new StateHistoryDto { StateName = "Created", CreatedAt = job.CreatedAt ?? default(DateTime) });

        job.History = historyList;
    }

    title = title ?? Strings.Common_Job;
    Layout = new LayoutPage(title);
}

<div class="row">
    <div class="col-md-3">
        @Html.JobsSidebar()
    </div>
    <div class="col-md-9">
        <h1 class="page-header">@title</h1>

        @if (job == null)
        {
            <div class="alert alert-warning">
                @String.Format(Strings.JobDetailsPage_JobExpired, JobId)
            </div>
        }
        else
        {
            var currentState = job.History[0];
            if (currentState.StateName == ProcessingState.StateName)
            {
                var server = monitor.Servers().FirstOrDefault(x => x.Name == currentState.Data["ServerId"]);
                if (server == null)
                {
                    <div class="alert alert-danger">
                        @Html.Raw(String.Format(Strings.JobDetailsPage_JobAbortedNotActive_Warning_Html, currentState.Data["ServerId"], Url.To("/servers")))
                    </div>
                }
                else if (server.Heartbeat.HasValue && server.Heartbeat < (StorageUtcNow ?? ApplicationUtcNow).AddMinutes(-1))
                {
                    <div class="alert alert-warning">
                        @Html.Raw(String.Format(Strings.JobDetailsPage_JobAbortedWithHeartbeat_Warning_Html, server.Name))
                    </div>
                }
            }

            if (job.ExpireAt.HasValue)
            {
                <div class="alert alert-info">
                    @Html.Raw(String.Format(Strings.JobDetailsPage_JobFinished_Warning_Html, JobHelper.ToTimestamp(job.ExpireAt.Value), job.ExpireAt))
                </div>
            }

            if (job.Job != null)
            {
                <div class="job-snippet">
                    <div class="job-snippet-code">
                        <pre><code><span class="comment">// @Strings.JobDetailsPage_JobId: @Html.JobId(JobId.ToString(), false)</span>
@if (job.Job.Queue != null)
{
<span class="comment">// @Strings.QueuesPage_Table_Queue: @job.Job.Queue</span>
}
@JobMethodCallRenderer.Render(job.Job)
</code></pre>
                    </div>
                </div>
            }
            else
            {
                var dbgParameters = job.Properties.Where(x => x.Key.StartsWith("DBG_")).ToArray();

                <div class="alert alert-warning">
                    <h4 id="job-details-missing-method">@Strings.Common_CannotFindTargetMethod</h4>
                    <table class="table table-condensed job-snippet-properties margin-bottom-0" aria-describedby="job-details-missing-method">
                        <tr>
                            <td>@Strings.JobDetailsPage_JobId</td>
                            <td><pre><code>@Html.JobId(JobId, false)</code></pre></td>
                        </tr>
                        @foreach (var parameter in dbgParameters)
                        {
                            <tr>
                                <td class="width-15">@parameter.Key.Substring(4)</td>
                                <td><pre><code>@parameter.Value</code></pre></td>
                            </tr>
                        }
                    </table>
                </div>
            }

            var displayParameters = job.Properties.Where(x => !x.Key.StartsWith("DBG_") && x.Key != "Continuations").ToArray();

            if (displayParameters.Length > 0)
            {
                <h3 id="job-details-parameters">@Strings.JobDetailsPage_Parameters</h3>
                <table class="table table-condensed job-snippet-properties" aria-describedby="job-details-parameters">
                    @foreach (var parameter in displayParameters)
                    {
                        <tr>
                            <td class="width-20 word-break">@parameter.Key</td>
                            <td><pre><code>@parameter.Value</code></pre></td>
                        </tr>
                    }
                </table>
            }

            if (job.Properties.TryGetValue("Continuations", out var serializedContinuations))
            {
                var continuations = ContinuationsSupportAttribute.DeserializeContinuations(serializedContinuations);

                if (continuations.Count > 0)
                {
                    <h3 id="job-details-continuations">@Strings.Common_Continuations</h3>
                    <div class="table-responsive">
                        <table class="table" aria-describedby="job-details-continuations">
                            <thead>
                            <tr>
                                <th class="min-width">@Strings.Common_Id</th>
                                <th class="min-width">@Strings.Common_Condition</th>
                                <th class="min-width">@Strings.Common_State</th>
                                <th>@Strings.Common_Job</th>
                                <th class="align-right">@Strings.Common_Created</th>
                            </tr>
                            </thead>
                            <tbody>
                            @foreach (var continuation in continuations)
                            {
                                JobData jobData;

                                using (var connection = Storage.GetReadOnlyConnection())
                                {
                                    jobData = connection.GetJobData(continuation.JobId);
                                }

                                <tr>
                                    @if (jobData == null)
                                    {
                                        <td colspan="5"><em>@String.Format(Strings.JobDetailsPage_JobExpired, continuation.JobId)</em></td>
                                    }
                                    else
                                    {
                                        <td class="min-width">@Html.JobIdLink(continuation.JobId)</td>
                                        <td class="min-width"><code>@continuation.Options.ToString("G")</code></td>
                                        <td class="min-width">@Html.StateLabel(jobData.State)</td>
                                        <td class="word-break">@Html.JobNameLink(continuation.JobId, jobData.Job)</td>
                                        <td class="align-right">@Html.RelativeTime(jobData.CreatedAt)</td>
                                    }
                                </tr>
                            }
                            </tbody>
                        </table>
                    </div>
                }
            }

            if (dto != null)
            {
                foreach (var renderer in JobDetailsRenderer.GetRenderers())
                {
                    try
                    {
                        @renderer.Item2(dto)
                    }
                    catch (Exception ex)
                    {
                        <h4 class="exception-type">@ex.GetType().Name</h4>
                        <p class="text-muted">@ex.Message</p>
                        <pre class="stack-trace">@Html.StackTrace(ex.StackTrace)</pre>
                    }
                }
            }

            <h3>
                @if (job.History.Count > 1)
                {
                    <span class="job-snippet-buttons pull-right">
                        @if (!IsReadOnly)
                        {
                            <button class="btn btn-sm btn-default"
                                    data-ajax="@Url.To("/jobs/actions/requeue/" + JobId)"
                                    data-loading-text="@Strings.Common_Enqueueing">
                                @Strings.JobDetailsPage_Requeue
                            </button>
                        }
                        @if (!IsReadOnly)
                        {
                            <button class="btn btn-sm btn-death"
                                    data-ajax="@Url.To("/jobs/actions/delete/" + JobId)"
                                    data-loading-text="@Strings.Common_Deleting"
                                    data-confirm="@Strings.JobDetailsPage_DeleteConfirm">
                                @Strings.Common_Delete
                            </button>
                        }
                    </span>
                }

                @Strings.JobDetailsPage_State
            </h3>

            var index = 0;

            foreach (var entry in job.History)
            {
                var accentColor = JobHistoryRenderer.GetForegroundStateColor(entry.StateName);
                var backgroundColor = JobHistoryRenderer.GetBackgroundStateColor(entry.StateName);
                var cardCss = index == 0 ? JobHistoryRenderer.GetStateCssSuffix(entry.StateName) : null;
                var cardStyle = index == 0 && cardCss == null ? $"border-color: {accentColor}" : null;
                var cardTitleStyle = index == 0 && cardCss == null ? $"color: {accentColor}" : null;
                var cardBackgroundStyle = index == 0 && cardCss == null ? $"background-color: {backgroundColor}" : null;

                <div class="state-card @(cardCss != null ? "state-card-state-" + cardCss : null)" style="@cardStyle">
                    <h4 class="state-card-title" style="@cardTitleStyle">
                        <small class="pull-right text-muted">
                            @if (index == job.History.Count - 1)
                            {
                                @Html.RelativeTime(entry.CreatedAt)
                            }
                            else
                            {
                                var duration = Html.ToHumanDuration(entry.CreatedAt - job.History[index + 1].CreatedAt);

                                if (index == 0)
                                {
                                    @: @Html.RelativeTime(entry.CreatedAt) (@duration)
                                }
                                else
                                {
                                    @: @Html.MomentTitle(entry.CreatedAt, duration)
                                }
                            }
                        </small>

                        @entry.StateName
                    </h4>

                    @if (!String.IsNullOrWhiteSpace(entry.Reason))
                    {
                        <p class="state-card-text text-muted">@entry.Reason</p>
                    }

                    @{
                        var rendered = Html.RenderHistory(entry.StateName, entry.Data);
                    }

                    @if (rendered != null)
                    {
                        <div class="state-card-body" style="@cardBackgroundStyle">
                            @rendered
                        </div>
                    }
                </div>

                index++;
            }
        }
    </div>
</div>